/*
 * Copyright (c) 2007, David A. Lindquist (stringify.com)
 * Some Rights Reserved
 *
 * This code is licensed under the Creative Commons Attribution 2.5 License
 * (http://creativecommons.org/licenses/by/2.5/). Please maintain the above
 * license and copyright statements when using this code.
 *
 * $Id: columnav.js 6 2007-02-09 00:15:04Z david $
 */
YAHOO.namespace('extension');

YAHOO.extension.ColumNav = function(id, prevId, source) {
    this._init(id, prevId, source);
};

YAHOO.extension.ColumNav.prototype = {

    DOM: YAHOO.util.Dom,
    EVT: YAHOO.util.Event,
    CON: YAHOO.util.Connect,

    ERROR_MSG: 'data unavailable',

    // PUBLIC API

    reset: function() {
        this.carousel.clear();
        this._init(this.id, this.prevId, this.source);
    },

    // PRIVATE API

    _init: function(id, prevId, source) {
        this.id = id;
        this.prevId = prevId;
        this.source = source;
        this.request = null;
        this.counter = 1;
        this.moving = false;
        this.carousel = new YAHOO.extension.Carousel(id,
                            {
                                'numVisible':               1,
                                'scrollInc':                1,
                                'prevElement':              prevId,
                                'loadPrevHandler':          this._loadPrevHandler,
                                'animationCompleteHandler': this._animationCompleteHandler
                            });
        this.carousel.cn = this;

        if (source && source.nodeType == 1)
            this._addMenu(source);
        else if (typeof source == 'string')
            this._makeRequest(source);
        else
            this._handleFailure({});
    },

    _loadPrevHandler: function(type, args) {
        this.cn._abortRequest();
    },

    _makeRequest: function(url, scroll) {
        var callback = {
            'success':  this._handleSuccess,
            'failure':  this._handleFailure,
            'scope':    this,
            'argument': scroll,
            'timeout':  5000
        };
        this._abortRequest();
        this.request = this.CON.asyncRequest('GET', url, callback);
    },

    _abortRequest: function() {
        if (this.request && this.CON.isCallInProgress(this.request))
            this.CON.abort(this.request);
    },

    _handleSuccess: function(o) {
        this._addMenu(o.responseXML.documentElement, o.argument);
    },

    _handleFailure: function(o) {
        var list = document.createElement('ul');
        var item = document.createElement('li');
        var span = document.createElement('span');
        span.setAttribute('class', 'columnav-error');
        span.appendChild(document.createTextNode(this.ERROR_MSG));
        item.appendChild(span);
        list.appendChild(item);
        this._addMenu(list, o.argument);
    },

    _addMenu: function(node, scroll) {
        var menu = this._createMenu(node);
        this.carousel.addItem(this.counter++, menu);
        if (scroll) {
            this.carousel.scrollNext();
            this.moving = true;
        }
    },

    _next: function(e, ctx) {
        var list = ctx['list'];
        var href = ctx['href'];
        var rel  = ctx['rel'];
        if (!this.moving) {
            if (href !== null)
                this._highlight(this.EVT.getTarget(e));
            if (list)
                this._addMenu(list, true);
            else if (rel == 'ajax')
                this._makeRequest(href, true);
            else
                return;
        }
        this.EVT.preventDefault(e);
    },

    _highlight: function(target) {
        if ('span' == target.tagName.toLowerCase())
            target = target.parentNode;
        var items = this._getNodes(target.parentNode, this._childElements);
        for (var i = 0; i < items.length; i++)
            this.DOM.removeClass(items[i], 'columnav-active');
        this.DOM.addClass(target, 'columnav-active');
    },

    _animationCompleteHandler: function(type, args) {
        this.cn.moving = false;
        if (args[0] == 'prev')
            this.cn._removeLastMenu();
    },

    _removeLastMenu: function() {
        var list = this.carousel.carouselList;
        list.removeChild(list.lastChild);
        this.counter--;
    },

    _createMenu: function(node) {
        var menu = document.createElement('div');
        var items = this._getNodes(node, this._childElements);
        for (var i = 0; i < items.length; i++) {
            var ce = this._getNodes(items[i], this._childElements);
            var link = ce[0], list = ce[1];
            var text = link.firstChild.data;
            var href = link.getAttribute('href');
            var rel = link.getAttribute('rel');
            var cls = link.getAttribute('class');
            var a = document.createElement('a');
            var span = document.createElement('span');
            var context = { 'list': list, 'href': href, 'rel': rel };
            span.appendChild(document.createTextNode(text));
            a.appendChild(span);
            a.setAttribute('href', href || 'javascript:void(0)');
            if (cls)
                this.DOM.addClass(a, cls);
            if (list || rel == 'ajax')
                this.DOM.addClass(a, 'columnav-has-menu');
            this.EVT.addListener(a, 'click', this._next, context, this);
            menu.appendChild(a);
        }
        return menu;
    },

    _getNodes: function(root, filter) {
        var node = root;
        var nodes = [];
        var next;
        var f = filter || function() { return true; }
        while (node != null) {
            if (node.hasChildNodes())
                node = node.firstChild;
            else if (node != root && null != (next = node.nextSibling))
                node = next;
            else {
                next = null;
                for ( ; node != root; node = node.parentNode) {
                    next = node.nextSibling;
                    if (next != null) break;
                }
                node = next;
            }
            if (node != null && f(node, root))
                nodes.push(node);
        }
        return nodes;
    },

    _childElements: function(node, root) {
        return (node.nodeType == 1 && node.parentNode == root);
    }
};

YAHOO.extension.ColumnNav = YAHOO.extension.ColumNav;
